1 /*
2  * Hunt - a framework for web and console application based on Collie using Dlang development
3  *
4  * Copyright (C) 2015-2017  Shanghai Putao Technology Co., Ltd
5  *
6  * Developer: HuntLabs
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.i18n;
13 
14 import std.path;
15 import std.file;
16 import std.algorithm;
17 import std.array;
18 import kiss.logger;
19 import std.stdio;
20 import std.string;
21 
22 alias StrStr = string[string];
23 alias StrStrStr = string[string][string];
24 
25 enum I18N_DEFAULT_LOCALE = "zh-cn";
26 
27 ///国际化
28 class I18n
29 {
30 	private{
31 		StrStrStr _res;
32 		__gshared I18n _instance;
33 		string _default;
34 	}
35 	
36 	this()
37 	{
38 		_default = I18N_DEFAULT_LOCALE;
39 	}
40 	
41 	static I18n instance()
42 	{
43 		if(_instance is null)
44 		{
45 			_instance = new I18n();
46 		}
47 		return _instance;
48 	}
49 
50 	///加载资源文件
51 	bool loadLangResources(string path, lazy string ext = "res")
52 	{
53 		_isResLoaded = false;
54 		auto resfiles = std.file.dirEntries(path, "*.{res}", SpanMode.depth)
55 			.filter!(a => a.isFile)
56 				.map!(a => std.path.absolutePath(a.name))
57 				.array;
58 		if(resfiles.length == 0)
59 		{
60 			logDebug("lang res file is empty");
61 			return false;
62 		}
63 		
64 		foreach(r; resfiles)
65 		{
66 			parseResFile(r);
67 		}
68 		_isResLoaded = true;
69 		return true;
70 	}
71 
72 	@property bool isResLoaded() { return _isResLoaded; }
73 	private bool _isResLoaded = false;
74 	
75 	@property StrStrStr resources()
76 	{
77 		return this._res;
78 	}
79 
80 	///设置默认
81 	@property defaultLocale(string loc)
82 	{
83 		this._default = loc;
84 	}
85 
86 	@property string defaultLocale()
87 	{
88 		return this._default;
89 	}
90 	
91 	///解析文件
92 	private bool parseResFile(string fileName)
93 	{
94 		auto f = File(fileName,"r");
95 		scope(exit)
96 		{
97 			f.close();
98 		}
99 		
100 		if(!f.isOpen()) return false;
101 		
102 		string _res_file_name = baseName(fileName, extension(fileName));
103 		string _loc = baseName(dirName(fileName));
104 		
105 		int line = 1;
106 		while(!f.eof())
107 		{
108 			scope(exit) 
109 				line += 1;
110 			string str = f.readln();
111 			str = strip(str);
112 			if(str.length == 0) 
113 				continue;
114 			if(str[0] == '#' || str[0] == ';') 
115 				continue;
116 			auto len = str.length -1;
117 			
118 			auto site = str.indexOf("=");
119 			if(site == -1)
120 			{
121 				import std.format;
122 				throw new Exception(format("the format is erro in file %s, in line %d : string: %s",fileName,line, str));
123 			}
124 			string key = str[0..site].strip;
125 			if(key.length == 0)
126 			{
127 				import std.format;
128 				throw new Exception(format("the Key is empty in file %s, in line %d",fileName,line));
129 			}
130 			string value  = str[site + 1..$].strip;
131 			
132 			this._res[_loc][_res_file_name ~ "." ~ key] = value;
133 		}
134 		return true;
135 	}
136 	
137 }
138 
139 ///设置本地化
140 private string _local = I18N_DEFAULT_LOCALE;
141 
142 @property string getLocale(){
143 	if(_local)
144 		return _local;
145 	return I18n.instance().defaultLocale;
146 }
147 
148 ///设置本地化
149 @property setLocale(string _l)
150 { 
151 	_local = toLower(_l);
152 }
153 
154 ///key is [filename.key]
155 string getText(string key, lazy string default_value = string.init)
156 { 
157 	I18n i18n = I18n.instance();
158 	if(!i18n.isResLoaded)
159 	{
160 		logWarning("The lang resources has't loaded yet!");
161 		return key;
162 	}
163 
164 	auto p = getLocale in i18n.resources;
165 	if(p !is null)
166 	{
167 		return p.get(key, default_value);
168 	}
169 	logDebug("unsupported local: ", getLocale, ", use default now: ", i18n.defaultLocale);
170 	
171 	p = i18n.defaultLocale in i18n.resources;
172 	
173 	if(p !is null)
174 	{
175 		return p.get(key, default_value);
176 	}
177 	
178 	logDebug("unsupport local ", i18n.defaultLocale );
179 	
180 	return default_value;
181 }
182 
183 
184 
185 /*
186 unittest{
187 	
188 	I18n i18n = I18n.instance();
189 	i18n.loadLangResources("./resources/lang");
190 	i18n.defaultLocale = "en-us";
191 	writeln(i18n.resources);
192 	
193 	
194 	///
195 	setLocale("en-br");
196 	assert( getText("message.hello-world", "empty") == "Hello, world");
197 	
198 	///
199 	setLocale("zh-cn");
200 	assert( getText("email.subject", "empty") == "收件人");
201 	
202 	
203 	setLocale("en-us");
204 	assert( getText("email.subject", "empty") == "empty");
205 }
206 */